import { Component, OnInit, Injector, OnDestroy, ChangeDetectorRef, NgZone } from '@angular/core'
import { NavigationEnd, Router, RouterEvent, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router'
import { merge, Observable, Subject } from 'rxjs'
import { filter, takeUntil, startWith } from 'rxjs/operators'
import { BreadcrumbOption, NZ_ROUTE_DATA_BREADCRUMB } from 'ng-zorro-antd'
import { Menu, MenuService } from '@delon/theme'

import { I18NService } from '@core'

interface PageHeaderPath {
  title?: string
  link?: string[]
}

@Component({
  selector: 'app-header-breadcrumb',
  templateUrl: './header-breadcrumb.component.html',
  styleUrls: ['./header-breadcrumb.component.less'],
})
export class HeaderBreadcrumbComponent implements OnInit, OnDestroy {
  constructor(
    private injector: Injector,
    private cdr: ChangeDetectorRef,
    private ngZone: NgZone,
    private router: Router,
    private menuSrv: MenuService,
    private i18n: I18NService,
  ) {
    // tslint:disable-next-line:no-any
    const data$: Array<Observable<any>> = [
      router.events.pipe(filter((event: RouterEvent) => event instanceof NavigationEnd)),
    ]
    merge(...data$)
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe(() => {
        this.refresh()
      })
  }

  private get menus() {
    this._menus = this.menuSrv.getPathByUrl(
      this.router.url,
      this.recursiveBreadcrumb,
    )
    return this._menus
  }
  private _menus: Menu[] = []
  private unsubscribe$ = new Subject<void>()
  paths: PageHeaderPath[] = []
  recursiveBreadcrumb = true

  home = this.i18n.fanyi('GENERAL.INFO.HOME')
  homeLink = '/dashboard/user/profile'

  breadcrumbs: BreadcrumbOption[] | undefined = []

  private destroy$ = new Subject<void>()

  // 参考 https://github.com/ng-alain/delon/blob/master/packages/abc/page-header/page-header.component.ts
  private genBreadcrumb() {
    const paths: PageHeaderPath[] = []
    this.menus.forEach(item => {
      if (typeof item.hideInBreadcrumb !== 'undefined' && item.hideInBreadcrumb) {
        return
      }

      const title = item.text
      paths.push({ title, link: item.link && [item.link] })
    })

    // add home
    if (this.home) {
      paths.splice(0, 0, {
        title: this.home,
        link: [this.homeLink],
      })
    }

    this.paths = paths
  }

  ngOnInit() {
    this.refresh()
    this.registerRouterChange()
  }

  ngOnDestroy(): void {
    this.destroy$.next()
    this.destroy$.complete()
  }

  refresh() {
    this.genBreadcrumb()
    // 强制更新View
    // 参考 https://stackoverflow.com/questions/38445670/angular-2-view-will-not-update-after-variable-change-in-subscribe
    // if (!this.cdr.destroyed) {
    //   this.cdr.detectChanges()
    // }
  }

  navigate(url: string, e: MouseEvent): void {
    e.preventDefault()

    this.ngZone
      .run(() =>
        this.injector
          .get(Router)
          .navigateByUrl(url)
          .then(),
      )
      .then()
  }

  private registerRouterChange(): void {
    try {
      const router = this.injector.get(Router)
      const activatedRoute = this.injector.get(ActivatedRoute)
      router.events
        .pipe(
          filter(e => e instanceof NavigationEnd),
          takeUntil(this.destroy$),
          startWith(true), // Trigger initial render.
        )
        .subscribe(() => {
          this.breadcrumbs = this.getBreadcrumbs(activatedRoute.root)
          this.cdr.markForCheck()
        })
    } catch (e) {
      throw new Error(`You should import RouterModule if you want to use 'NzAutoGenerate'.`)
    }
  }

  private getBreadcrumbs(
    route: ActivatedRoute,
    url: string = '',
    breadcrumbs: BreadcrumbOption[] = [],
  ): BreadcrumbOption[] | undefined {
    const children: ActivatedRoute[] = route.children
    // If there's no sub root, then stop the recurse and returns the generated breadcrumbs.
    if (children.length === 0) {
      return breadcrumbs
    }
    for (const child of children) {
      if (child.outlet === PRIMARY_OUTLET) {
        // Only parse components in primary router-outlet (in another word, router-outlet without a specific name).
        // Parse this layer and generate a breadcrumb item.
        const routeURL: string = child.snapshot.url.map(segment => segment.path).join('/')
        const nextUrl = url + `/${routeURL}`
        const breadcrumbLabel = child.snapshot.data[NZ_ROUTE_DATA_BREADCRUMB]
        // If have data, go to generate a breadcrumb for it.
        if (routeURL && breadcrumbLabel) {
          const breadcrumb: BreadcrumbOption = {
            label: this.i18n.fanyi(breadcrumbLabel),
            params: child.snapshot.params,
            url: nextUrl,
          }
          breadcrumbs.push(breadcrumb)
        }
        return this.getBreadcrumbs(child, nextUrl, breadcrumbs)
      }
    }
  }
}
